#!/usr/bin/env python
# coding: UTF-8

'''This is the helper script to setup/manage your seafile server
'''

import sys

####################
### Requires Python 2.6+
####################
if sys.version_info.major == 3:
    print 'Python 3 not supported yet. Quit now'
    sys.exit(1)
if sys.version_info.minor < 6:
    print 'Python 2.6 or above is required. Quit now'
    sys.exit(1)

import os
import time
import re
import shutil
import subprocess
import argparse
import sqlite3
import uuid
import hashlib
import getpass

try:
    import readline
    # Avoid pylint 'unused import' warning
    dummy = readline
except ImportError:
    pass

####################
### Cosntants
####################
SERVER_MANUAL_HTTP = 'https://github.com/haiwen/seafile/wiki'
SEAFILE_GOOGLE_GROUP = 'https://groups.google.com/forum/?fromgroups#!forum/seafile'
SEAFILE_WEBSITE = 'http://www.seafile.com'
SEAHUB_DOWNLOAD_URL = 'https://seafile.com.cn/downloads/seahub-latest.tar.gz'

####################
### Global variables
####################
cwd = os.getcwd()
SCRIPT_NAME = os.path.basename(sys.argv[0])

PYTHON = sys.executable

conf = {}
CONF_SERVER_NAME = 'server_name'
CONF_CCNET_DIR = 'ccnet_dir'
CONF_SEAFILE_DIR = 'seafile_dir'
CONF_SEAHUB_DIR = 'seafile_dir'
CONF_CCNET_PORT = 'ccnet_port'
CONF_SEAFILE_PORT = 'seafile_port'
CONF_HTTPSERVER_PORT = 'httpserver_port'
CONF_IP_OR_DOMAIN = 'ip_or_domain'

CONF_SEAHUB_CONF = 'seahub_conf'
CONF_SEAHUB_DIR = 'seahub_dir'
CONF_SEAHUB_PORT = 'seahub_port'

CONF_SEAHUB_PIDFILE = 'seahub_pidfile'
CONF_SEAHUB_OUTLOG = 'seahub_outlog'
CONF_SEAHUB_ERRLOG = 'seahub_errlog'

CONF_CCNET_CONF_EXISTS = 'ccnet_conf_exists'
CONF_SEAFILE_CONF_EXISTS = 'seafile_conf_exists'

CONF_ADMIN_EMAIL = 'admin_email'
CONF_ADMIN_PASSWORD = 'admin_password'

####################
### Common helper functions

def highlight(content):
    '''Add ANSI color to content to get it highlighted on terminal'''
    return '\x1b[33m%s\x1b[m' % content

def info(msg):
    print msg

def error(msg):
    print 'Error: ' + msg
    sys.exit(1)

def ask_question(desc, key=None, note=None, default=None,
                 validate=None, yes_or_no=False, invalidate_msg=None):
    '''Ask a question, return the answer. The optional validate param is a
    function used to validate the answer. If yes_or_no is True, then a boolean
    value would be returned.

    '''
    assert key or yes_or_no
    desc = highlight(desc)
    if note:
        desc += '  (%s)' % note
    if default:
        desc += '\n' + ('[default %s ]' % default)
    else:
        if yes_or_no:
            desc += '\n[yes or no]'
        else:
            desc += '\n' + ('[%s ]' % key)

    desc += '  '
    while True:
        answer = raw_input(desc)
        if not answer:
            if default:
                print ''
                return default
            else:
                continue

        answer = answer.strip()

        if yes_or_no:
            if answer != 'yes' and answer != 'no':
                print '\nPlease answer yes or no\n'
                continue
            else:
                return answer == 'yes'
        else:
            if validate and not validate(answer):
                if invalidate_msg:
                    print '\n%s\n' % invalidate_msg
                else:
                    print '\n"%s" is not a valid %s\n' % (answer, key)
                continue

        print ''
        return answer

def run_argv(argv, cwd=None, env=None, suppress_stdout=False, suppress_stderr=False):
    '''Run a program and wait it to finish, and return its exit code. The
    standard output of this program is supressed.

    '''
    with open(os.devnull, 'w') as devnull:
        if suppress_stdout:
            stdout = devnull
        else:
            stdout = sys.stdout

        if suppress_stderr:
            stderr = devnull
        else:
            stderr = sys.stderr

        proc = subprocess.Popen(argv,
                                cwd=cwd,
                                stdout=stdout,
                                stderr=stderr,
                                env=env)
        return proc.wait()

def run(cmdline, cwd=None, env=None, suppress_stdout=False, suppress_stderr=False):
    '''Like run_argv but specify a command line string instead of argv'''
    with open(os.devnull, 'w') as devnull:
        if suppress_stdout:
            stdout = devnull
        else:
            stdout = sys.stdout

        if suppress_stderr:
            stderr = devnull
        else:
            stderr = sys.stderr

        proc = subprocess.Popen(cmdline,
                                cwd=cwd,
                                stdout=stdout,
                                stderr=stderr,
                                env=env,
                                shell=True)
        return proc.wait()

def is_running(process):
    '''Detect if there is a process with the given name running'''
    argv = [
        'pgrep', '-f', process
    ]

    return run_argv(argv, suppress_stdout=True) == 0

def pkill(process):
    '''Kill the program with the given name'''
    argv = [
        'pkill', '-f', process
    ]

    run_argv(argv)

def kill(pid):
    '''Kill the program with the given pid'''
    argv = [
        'kill', pid
    ]

    run_argv(argv)

def must_mkdir(path):
    '''Create a directory, exit on failure'''
    try:
        os.mkdir(path)
    except OSError, e:
        error('failed to create directory %s:%s' % (path, e))

### END of Common helper functions
####################

def check_seafile_install():
    '''Check if seafile has been correctly built and installed in this
    system

    '''
    dirs = os.environ['PATH'].split(':')
    def exist_in_path(prog):
        '''Test whether prog exists in system path'''
        for d in dirs:
            if d == '':
                continue
            path = os.path.join(d, prog)
            if os.path.exists(path):
                return True

        return False

    def check_prog(name):
        if not exist_in_path(name):
            error('%s not found in PATH. Have you built and installed seafile server?' % name)

    progs = [
        'ccnet-init',
        'seaf-server-init',
        'seaf-server',
        'ccnet-server',
        'httpserver',
        'seafile-controller',
    ]

    for prog in progs:
        check_prog(prog)

def get_seahub_env():
    '''And PYTHONPATH and CCNET_CONF_DIR/SEAFILE_CONF_DIR to env, which is
    needed by seahub

    '''
    seahub_dir = conf[CONF_SEAHUB_DIR]
    seahub_thirdpart_dir = os.path.join(seahub_dir, 'thirdpart')

    env = dict(os.environ)
    pypath = env.get('PYTHONPATH', '')

    pathlist = [ p for p in pypath.split(':') if p != '' ]
    pathlist.append(seahub_thirdpart_dir)
    newpypath = ':'.join(pathlist)
    env['PYTHONPATH'] = newpypath
    env['CCNET_CONF_DIR'] = conf[CONF_CCNET_DIR]
    env['SEAFILE_CONF_DIR'] = conf[CONF_SEAFILE_DIR]
    return env


####################
### <setup> command
####################
def welcome():
    '''Show welcome message when running the <setup> command'''
    welcome_msg = '''\
-----------------------------------------------------------------
This script will guide you to config and setup your seafile server.
Make sure you have read seafile server manual at

        %s

Press [ENTER] to continue
-----------------------------------------------------------------
''' % SERVER_MANUAL_HTTP
    print welcome_msg
    raw_input()

def get_server_name():
    def validate(name):
        r = re.compile(r'^[\w]{3,15}$')
        return bool(r.match(name))
    question = 'What is the name of the server?'
    key = 'server name'
    note = '3 - 15 letters or digits'
    conf[CONF_SERVER_NAME] = ask_question(question,
                                          key=key,
                                          note=note,
                                          validate=validate)

def get_server_ip_or_domain():
    def validate(s):
        r = r'^[^.].+\..+[^.]$'
        return bool(re.match(r, s))
    question = 'What is the ip of the server?'
    key = 'ip or domain'
    note = 'For example: www.mycompany.com, 192.168.1.101'
    conf[CONF_IP_OR_DOMAIN] = ask_question(question,
                                           key=key,
                                           note=note,
                                           validate=validate)
def get_ccnet_conf_dir():
    ccnet_conf_dir = os.path.join(cwd, 'ccnet')
    ccnet_conf = os.path.join(ccnet_conf_dir, 'ccnet.conf')

    if os.path.exists(ccnet_conf_dir):
        if os.path.exists(ccnet_conf):
            question = 'It seems there already exists ccnet config files in %s, Do you want to use them?' % ccnet_conf_dir
            yesno = ask_question(question, yes_or_no=True)
            if not yesno:
                print highlight('\nRemove the directory %s first, and run the script again.\n' % ccnet_conf_dir)
                sys.exit(1)
            else:
                conf[CONF_CCNET_CONF_EXISTS] = True
        else:
            print 'Please remove the directory %s first, and run the script again' % ccnet_conf_dir
    else:
        conf[CONF_CCNET_CONF_EXISTS] = False

    conf[CONF_CCNET_DIR] = ccnet_conf_dir

def get_ccnet_port():
    def validate(s):
        try:
            port = int(s)
        except ValueError:
            return False

        return port > 0 and port < 65536

    question = 'Which port do you want to use for the ccnet server?'
    key = 'ccnet server port'
    default = '10001'
    conf[CONF_CCNET_PORT] = ask_question(question,
                                         key=key,
                                         default=default,
                                         validate=validate)

def get_seafile_port():
    def validate(s):
        try:
            port = int(s)
        except ValueError:
            return False

        return port > 0 and port < 65536

    question = 'Which port do you want to use for the seafile server?'
    key = 'seafile server port'
    default = '12001'
    conf[CONF_SEAFILE_PORT] = ask_question(question,
                                           key=key,
                                           default=default,
                                           validate=validate)

def get_httpserver_port():
    def validate(s):
        try:
            port = int(s)
        except ValueError:
            return False

        return port > 0 and port < 65536

    question = 'Which port do you want to use for the seafile httpserver?'
    key = 'seafile httpserver port'
    default = '8082'
    conf[CONF_HTTPSERVER_PORT] = ask_question(question,
                                              key=key,
                                              default=default,
                                              validate=validate)
def get_seafile_data_dir():
    question = 'Where do you want to put your seafile data?'
    key = 'seafile-data'
    note = 'Please use a volume with enough free space'
    default = os.path.join(cwd, 'seafile-data')
    seafile_data_dir = ask_question(question,
                                    key=key,
                                    note=note,
                                    default=default)
    seafile_conf = os.path.join(seafile_data_dir, 'seafile.conf')

    if os.path.exists(seafile_data_dir):
        if os.path.exists(seafile_conf):
            question = 'It seems there already exists seafile data in %s, Do you want to use them?' % seafile_data_dir
            yesno = ask_question(question, yes_or_no=True)
            if not yesno:
                print highlight('\nRemove the directory %s first, and run the script again.\n' % seafile_data_dir)
                sys.exit(1)
            else:
                conf[CONF_SEAFILE_CONF_EXISTS] = True
        else:
            info('Please remove the directory %s first, and run the script again' % seafile_data_dir)
    else:
        conf[CONF_SEAFILE_CONF_EXISTS] = False

    conf[CONF_SEAFILE_DIR] = seafile_data_dir


def create_gunicorn_conf():
    runtime_dir = os.path.join(cwd, 'seafile-server', 'runtime')
    confpath = os.path.join(runtime_dir, 'seahub.conf')

    if os.path.exists(confpath):
        return

    if not os.path.exists(runtime_dir):
        must_mkdir(runtime_dir)

    content = '''\
import os
daemon = True
workers = 3

# Logging
runtime_dir = os.path.dirname(__file__)
pidfile = os.path.join(runtime_dir, 'seahub.pid')
errorlog = os.path.join(runtime_dir, 'error.log')
accesslog = os.path.join(runtime_dir, 'access.log')
'''
    try:
        with open(confpath, 'w') as fp:
            fp.write(content)
    except:
        error('Failed to write seahub config')

def get_admin_email_password():
    info('')
    info("Now let\'s create the admin account of seahub")
    info('')
    def validate(email):
        # whitespace is not allowed
        if re.match(r'[\s]', email):
            return False
        # must be a valid email address
        if not re.match(r'^.+@.*\..+$', email):
            return False
        return True

    key = 'admin email'
    question = 'What is the ' + highlight('email') + ' for the admin account'
    admin_email = ask_question(question,
                               key=key,
                               validate=validate)

    key = 'admin password'
    question = 'What is the ' + highlight('password') + ' for the admin account'
    admin_password = ask_question(question,
                                  key=key)

    key = 'admin password again'
    question = 'Enter the password again'
    invalidate_msg = 'Password mismatch'
    def validate_again(password):
        return password == admin_password
    ask_question(question,
                 key=key,
                 validate=validate_again,
                 invalidate_msg=invalidate_msg)

    info('This is your admin account email/password:\n')
    info('------------------------------------------')
    info('admin email:         %s' % admin_email)
    info('admin password:      %s' % admin_password)
    info('------------------------------------------')
    info('Press ENTER if the config is right, or anything else to re-config admin account')

    if raw_input() != '':
        get_admin_email_password()
    else:
        sha1 = hashlib.sha1(admin_password)
        conf[CONF_ADMIN_EMAIL] = admin_email
        conf[CONF_ADMIN_PASSWORD] = sha1.hexdigest()

def create_seahub_admin():

    peermgr_dir = os.path.join(conf[CONF_CCNET_DIR], 'PeerMgr')
    usermgr_db = os.path.join(peermgr_dir, 'usermgr.db')

    if os.path.exists(usermgr_db):
        return

    if not os.path.exists(peermgr_dir):
        os.mkdir(peermgr_dir)

    get_admin_email_password()

    conn = sqlite3.connect(usermgr_db)
    c = conn.cursor()

    sql = 'CREATE TABLE IF NOT EXISTS EmailUser (id INTEGER NOT NULL PRIMARY KEY, email TEXT, passwd TEXT, is_staff bool NOT NULL, is_active bool NOT NULL, ctime INTEGER)'
    c.execute(sql)
    sql = r'INSERT INTO EmailUser(email, passwd, is_staff, is_active, ctime) VALUES ("%s", "%s", 1, 1, 0)' \
          % (conf[CONF_ADMIN_EMAIL], conf[CONF_ADMIN_PASSWORD])
    c.execute(sql)
    conn.commit()

    info('Successfully created your admin account')

def gen_seahub_secret_key():
    data = str(uuid.uuid4()) + str(uuid.uuid4())
    return data[:40]

def create_seahub_settings_py():
    seahub_settings_py = os.path.join(cwd, 'seahub_settings.py')
    try:
        with open(seahub_settings_py, 'w') as fp:
            line = "SECRET_KEY = '%s'" % gen_seahub_secret_key()
            fp.write(line)
    except Exception, e:
        error('failed to create %s: %s' % (seahub_settings_py, e))

def move_avatar():
    seahub_data_dir = os.path.join(cwd, 'seahub-data')
    outside_avatar_dir = os.path.join(seahub_data_dir, 'avatars')
    seahub_avatar_dir = os.path.join(conf[CONF_SEAHUB_DIR], 'media', 'avatars')

    if os.path.exists(outside_avatar_dir):
        return

    if not os.path.exists(seahub_data_dir):
        must_mkdir(seahub_data_dir)

    # move the avatars dir outside
    shutil.move(seahub_avatar_dir, outside_avatar_dir)
    # make the the original avatars dir a symlink pointing to the outside dir
    os.symlink(outside_avatar_dir, seahub_avatar_dir)

def init_seahub():
    seahub_dir = conf[CONF_SEAHUB_DIR]

    # create seahub_settings.py
    create_seahub_settings_py()

    argv = [PYTHON, 'manage.py', 'syncdb']
    # Set proper PYTHONPATH before run django syncdb command
    env = get_seahub_env()

    print
    print
    info('Now initializing seahub database, please wait...')
    print

    if run_argv(argv, cwd=seahub_dir, env=env) != 0:
        error('Seahub syncdb failed')

    info('done')

    create_seahub_admin()
    move_avatar()
    create_gunicorn_conf()

def check_django_version():
    '''Requires django 1.5'''
    import django
    if django.VERSION[1] != 5:
        error('Django 1.5 is required')
    else:
        del django

def check_python_module(import_name, package_name, silent=False):
    if not silent:
        info('checking %s' % package_name)
    try:
        __import__(import_name)
    except ImportError:
        error('Python module "%s" not found. Please install it first' % package_name)

def check_python_dependencies(silent=False):
    '''Ensure all python libraries we need are installed'''

    if not silent:
        info('check python modules ...')
    check_python_module('simplejson', 'simplejson', silent=silent)
    check_python_module('sqlite3', 'sqlite3', silent=silent)
    check_python_module('PIL', 'python imaging library(PIL)', silent=silent)
    check_python_module('django', 'django 1.5', silent=silent)
    check_python_module('djblets', 'djblets', silent=silent)
    check_django_version()

    print

def config_ccnet_seafile():
    get_ccnet_conf_dir()
    if not conf[CONF_CCNET_CONF_EXISTS]:
        get_server_name()
        get_server_ip_or_domain()
        get_ccnet_port()

    get_seafile_data_dir()
    if not conf[CONF_SEAFILE_CONF_EXISTS]:
        get_seafile_port()
        get_httpserver_port()

    info('This is your configuration')
    info('------------------------------------------')
    if conf[CONF_CCNET_CONF_EXISTS]:
        info('ccnet config:        use existing config in %s' % highlight(conf[CONF_CCNET_DIR]))
    else:
        info('ccnet conf dir:           %s' % highlight(conf[CONF_CCNET_DIR]))
        info('server name:              %s' % highlight(conf[CONF_SERVER_NAME]))
        info('server host:              %s' % highlight(conf[CONF_IP_OR_DOMAIN]))
        info('ccnet port:               %s' % highlight(conf[CONF_CCNET_PORT]))

    if conf[CONF_SEAFILE_CONF_EXISTS]:
        info('seafile:             use existing config in %s' % highlight(conf[CONF_SEAFILE_DIR]))
    else:
        info('seafile data dir:         %s' % highlight(conf[CONF_SEAFILE_DIR]))
        info('seafile port:             %s' % highlight(conf[CONF_SEAFILE_PORT]))
        info('seafile httpserver port:  %s' % highlight(conf[CONF_HTTPSERVER_PORT]))

    info('------------------------------------------')
    info('Press ENTER if the config is right, or anything else to re-config ')

    if raw_input() != '':
        config_ccnet_seafile()
    else:
        return

def init_ccnet_seafile():
    if not conf[CONF_CCNET_CONF_EXISTS]:
        info('Generating ccnet configuration...')
        argv = [
            'ccnet-init',
            '-c', conf[CONF_CCNET_DIR],
            '--name', conf[CONF_SERVER_NAME],
            '--port', conf[CONF_CCNET_PORT],
            '--host', conf[CONF_IP_OR_DOMAIN],
        ]

        if run_argv(argv) != 0:
            error('failed to init ccnet configuration')

        info('done')

    if not conf[CONF_SEAFILE_CONF_EXISTS]:
        info('Generating seafile configuration...')
        argv = [
            'seaf-server-init',
            '--seafile-dir', conf[CONF_SEAFILE_DIR],
            '--port', conf[CONF_SEAFILE_PORT],
            '--httpserver-port', conf[CONF_HTTPSERVER_PORT],
        ]

        if run_argv(argv) != 0:
            error('failed to init seafile configuration')

        info('done')

    seafile_ini = os.path.join(conf[CONF_CCNET_DIR], 'seafile.ini')
    with open(seafile_ini, 'w') as fp:
        fp.write(conf[CONF_SEAFILE_DIR])

####################
### <start> command
####################
def start_controller():
    argv = [
        'seafile-controller',
        '-c', conf[CONF_CCNET_DIR],
        '-d', conf[CONF_SEAFILE_DIR],
    ]

    info('Starting seafile-server...')
    if run_argv(argv) != 0:
        error('Failed to start seafile')

    # check again after several seconds
    time.sleep(10)

    if not is_running('seafile-controller'):
        error('Failed to start seafile')

def start_seahub_gunicorn():
    argv = [
        'gunicorn_django',
        '-c', conf[CONF_SEAHUB_CONF],
        '-b', '0.0.0.0:%s' % conf[CONF_SEAHUB_PORT],
    ]

    info('Starting seahub...')
    env = get_seahub_env()
    if run_argv(argv, cwd=conf[CONF_SEAHUB_DIR], env=env) != 0:
        error('Failed to start seahub')

    info('Seahub running on port %s' % conf[CONF_SEAHUB_PORT])

def start_seahub_fastcgi():
    info('Starting seahub in fastcgi mode...')
    argv = [
        PYTHON, 'manage.py', 'runfcgi',
        'host=127.0.0.1',
        'port=%(port)s',
        'pidfile=%(pidfile)s',
        'outlog=%(outlog)s',
        'errlog=%(errlog)s',
    ]

    cmdline = ' '.join(argv) % \
              dict(port=conf[CONF_SEAHUB_PORT],
                   pidfile=conf[CONF_SEAHUB_PIDFILE],
                   outlog=conf[CONF_SEAHUB_OUTLOG],
                   errlog=conf[CONF_SEAHUB_ERRLOG])

    env = get_seahub_env()

    if run(cmdline, cwd=conf[CONF_SEAHUB_DIR], env=env) != 0:
        error('Failed to start seahub in fastcgi mode')

    info('Seahub running on port %s (fastcgi)' % conf[CONF_SEAHUB_PORT])


def read_seafile_data_dir(ccnet_conf_dir):
    '''Read the location of seafile-data from seafile.ini, also consider the
    upgrade from older version which do not has the seafile.ini feature

    '''
    seafile_ini = os.path.join(ccnet_conf_dir, 'seafile.ini')
    if os.path.exists(seafile_ini):
        with open(seafile_ini, 'r') as fp:
            seafile_data_dir = fp.read()
    else:
        # In previous seafile-admin, seafiled-data folder must be under
        # the top level directory, so we do not store the location of
        # seafile-data folder in seafile.ini
        seafile_data_dir = os.path.join(cwd, 'seafile-data')
        if os.path.exists(seafile_data_dir):
            with open(seafile_ini, 'w') as fp:
                fp.write(seafile_data_dir)

    return seafile_data_dir

def check_config(args):
    def error_not_found(path):
        error('%s not found' % path)
    ccnet_conf_dir = os.path.join(cwd, 'ccnet')
    if not os.path.exists(ccnet_conf_dir):
        error_not_found(ccnet_conf_dir)

    ccnet_conf = os.path.join(ccnet_conf_dir, 'ccnet.conf')
    if not os.path.exists(ccnet_conf):
        error_not_found(ccnet_conf)

    seafile_data_dir = read_seafile_data_dir(ccnet_conf_dir)
    if not os.path.exists(seafile_data_dir):
        error_not_found(seafile_data_dir)

    seafile_conf = os.path.join(seafile_data_dir, 'seafile.conf')
    if not os.path.exists(seafile_conf):
        error_not_found(seafile_conf)

    runtime_dir = os.path.join(cwd, 'seafile-server', 'runtime')
    seahub_conf = os.path.join(runtime_dir, 'seahub.conf')
    if not os.path.exists(seahub_conf):
        error_not_found(seahub_conf)

    seahub_dir = os.path.join(cwd, 'seafile-server', 'seahub')
    if not os.path.exists(seahub_conf):
        error_not_found(seahub_dir)

    try:
        port = int(args.port)
    except ValueError:
        error('invalid port: %s' % args.port)
    else:
        if port <= 0 or port > 65535:
            error('invalid port: %s' % args.port)

    conf[CONF_CCNET_DIR]        = ccnet_conf_dir
    conf[CONF_SEAFILE_DIR]      = seafile_data_dir
    conf[CONF_SEAHUB_DIR]       = seahub_dir
    conf[CONF_SEAHUB_CONF]      = seahub_conf
    conf[CONF_SEAHUB_PORT]      = port
    conf[CONF_SEAHUB_PIDFILE]   = os.path.join(runtime_dir, 'seahub.pid')
    conf[CONF_SEAHUB_OUTLOG]    = os.path.join(runtime_dir, 'access.log')
    conf[CONF_SEAHUB_ERRLOG]    = os.path.join(runtime_dir, 'error.log')

def check_directory_layout():
    seaf_server_dir = os.path.join(cwd, 'seafile-server')
    if not os.path.exists(seaf_server_dir):
        error('"seafile-server/" not found in current directory. \nPlease run seafile-admin in the correct directory.')

    seahub_dir = os.path.join(seaf_server_dir, 'seahub')
    if not os.path.exists(seahub_dir):
        error('"seafile-server/seahub/" not found. \nPlease download seahub first.')

    conf[CONF_SEAHUB_DIR] = seahub_dir

def setup_seafile(args):
    # avoid pylint "unused variable" warning
    dummy = args

    welcome()
    check_python_dependencies()
    config_ccnet_seafile()
    init_ccnet_seafile()
    init_seahub()

    print
    print '-----------------------------------------------------------------'
    print '-----------------------------------------------------------------'
    print 'Your seafile server configuration has been finished successfully.'
    print '-----------------------------------------------------------------'
    print '-----------------------------------------------------------------'
    print
    print 'To start/stop seafile server:'
    print
    print highlight('         $ cd %s' % cwd)
    print highlight('         $ %s { start | stop }' % SCRIPT_NAME)
    print
    print 'If you have any problem, refer to\n'
    print
    print ' Seafile server manual:      %s' % SERVER_MANUAL_HTTP
    print
    print ' Seafile discussion group:   %s' % SEAFILE_GOOGLE_GROUP
    print
    print ' Seafile website:            %s' % SEAFILE_WEBSITE
    print
    print 'for more information.'
    print

def check_necessary_files():
    files = [
        os.path.join(cwd, 'ccnet', 'ccnet.conf'),
        os.path.join(cwd, 'seafile-server', 'runtime', 'seahub.conf'),
        os.path.join(cwd, 'seahub.db'),
        os.path.join(cwd, 'seahub_settings.py'),
    ]

    for fpath in files:
        if not os.path.exists(fpath):
            error('%s not found' % fpath)

def start_seafile(args):
    '''start ccnet/seafile/seahub/httpserver'''
    if is_running('seafile-controller'):
        error(highlight('NOTE: Seafile is already running'))

    check_python_dependencies(silent=True)
    if args.fastcgi:
        check_python_module('flup', 'flup', silent=True)
    else:
        check_python_module('gunicorn', 'gunicorn', silent=True)

    check_necessary_files()

    check_config(args)

    start_controller()

    if args.port:
        try:
            port = int(args.port)
        except ValueError:
            error('invalid port: %s' % args.port)
        else:
            if port <= 0 or port > 65535:
                error('invalid port: %s' % args.port)

    if args.fastcgi:
        start_seahub_fastcgi()
    else:
        start_seahub_gunicorn()

    info('Done')

def stop_seafile(dummy):
    info('Stopping seafile server')
    pkill('seafile-controller')
    runtime_dir = os.path.join(cwd, 'seafile-server', 'runtime')
    pidfile = os.path.join(runtime_dir, 'seahub.pid')
    try:
        with open(pidfile, 'r') as fp:
            pid = fp.read().strip('\n ')
            if pid:
                kill(pid)
    except:
        pass

    info('done')

def reset_admin(dummy):
    '''reset seafile admin account'''
    # Get .ccnet directory from argument or user input
    ccnet_dir = os.path.join(cwd, 'ccnet')

    # Test usermgr.db exists
    usermgr_db = os.path.join(ccnet_dir, 'PeerMgr/usermgr.db')
    if not os.path.exists(usermgr_db):
        error('%s NOT exists. FAILED' % usermgr_db)

    conn = sqlite3.connect(usermgr_db)
    c = conn.cursor()

    # Check whether admin user exists
    sql = 'SELECT email FROM EmailUser WHERE is_staff = 1'
    try:
        c.execute(sql)
    except sqlite3.Error, e:
        error('An error orrured: %s' % e.args[0])

    staff_list = c.fetchall()
    if staff_list:
        print 'Admin is already in database. Email as follows: '
        print '--------------------'
        for e in staff_list:
            print e[0]
        print '--------------------'
        choice = raw_input('Previous admin would be deleted, would you like to continue?[y/n] ')
        if choice == 'y':
            sql = 'DELETE FROM EmailUser WHERE is_staff = 1'
            try:
                c.execute(sql)
            except sqlite3.Error, e:
                error('An error orrured: %s' % e.args[0])
            else:
                info('Previous admin is deleted')
        else:
            conn.close()
            sys.exit(0)

    # Create admin user
    choice = raw_input('Would you like to create admin user?[y/n]')
    if choice != 'y':
        conn.close()
        sys.exit(0)

    username = raw_input('E-mail address:')
    passwd = getpass.getpass('Password:')
    passwd2 = getpass.getpass('Password (again):')
    if passwd != passwd2:
        error('Two passwords are not the same')

    enc_passwd = hashlib.sha1(passwd).hexdigest()
    sql = "INSERT INTO EmailUser(email, passwd, is_staff, is_active, ctime) VALUES ('%s', '%s', 1, 1, '%d');" % (username, enc_passwd, time.time()*1000000)
    try:
        c = conn.cursor()
        c.execute(sql)
        conn.commit()
    except sqlite3.Error, e:
        error('An error orrured: %s' % e.args[0])
    else:
        info('Admin user created successfully')

    # Close db
    conn.close()

def main():
    check_seafile_install()
    check_directory_layout()

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(title='subcommands',
                                       description='')

    parser_setup = subparsers.add_parser('setup', help='setup the seafile server')
    parser_setup.set_defaults(func=setup_seafile)

    parser_start = subparsers.add_parser('start', help='start the seafile server')
    parser_start.set_defaults(func=start_seafile)

    parser_start.add_argument('--fastcgi', help='start seahub in fastcgi mode',
                              action='store_true')

    parser_start.add_argument('--port', help='start seahub in fastcgi mode',
                              default='8000')

    parser_stop = subparsers.add_parser('stop', help='stop the seafile server')
    parser_stop.set_defaults(func=stop_seafile)

    parser_reset_admin = subparsers.add_parser('reset-admin', help='reset seafile admin account')
    parser_reset_admin.set_defaults(func=reset_admin)

    args = parser.parse_args()
    args.func(args)

if __name__ == '__main__':
    main()